package index;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import xxl.core.io.Convertable;
import xxl.core.spatial.points.Point;

public class SparseMatrix implements Point, Convertable, Cloneable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static final int ROW_SIZE=1024;
	public static final int COL_SIZE=16; 
	
	private int rowIndex_from;
	private int rowIndex_to; 
	private float[] matrix;
	
	private long objectId;  
	
	public long getObjectId() {
		return objectId;
	}
	public void setObjectId(long objectId) {
		this.objectId = objectId;
	}
	transient private float[] row_buffer;
	public SparseMatrix(){
		row_buffer=new float[COL_SIZE]; 
	}
	public SparseMatrix(int from, int to){
		rowIndex_from=from; 
		rowIndex_to=to; 
		matrix=new float[(to-from+1)*COL_SIZE];
		row_buffer=new float[COL_SIZE]; 
	}
	public int getRowIndex_to() {
		return rowIndex_to;
	}
	public void setRow(int rowId, float[] data){
		if (rowId<rowIndex_from || rowId>rowIndex_to){
			throw new RuntimeException("rowId out of range"); 
		}
		System.arraycopy(data, 0, matrix, (rowId-rowIndex_from)*COL_SIZE , COL_SIZE); 
	}
	/** Returns a physical copy of this DoublePoint.
	 * @return returns a physical copy of this DoublePoint
	 */
	public Object clone(){ 
		SparseMatrix m=new SparseMatrix(this.rowIndex_from,this.rowIndex_to);
		m.matrix=(float[])matrix.clone();
		m.setObjectId(this.getObjectId()); 
		return m; 
	}
	public float[] getRow(int rowId){
		System.arraycopy(matrix, (rowId-rowIndex_from)*COL_SIZE, row_buffer, 0, COL_SIZE); 
		return row_buffer; 
	}
	public int getRowIndex_from() {
		return rowIndex_from;
	}
	public static void main(String[] args){
		SparseMatrix m=new SparseMatrix(10,20); 
		float[] row=new float[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; 
		m.setRow(12, row);
		float[] ret=m.getRow(12);
		for (int i=0; i<ret.length; i++){
			System.out.println(ret[i]); 
		}
	}
	@Override
	public void read(DataInput dataInput) throws IOException {
		this.objectId=dataInput.readLong(); 
		this.rowIndex_from=dataInput.readInt(); 
		this.rowIndex_to=dataInput.readInt(); 
		matrix=new float[(rowIndex_to-rowIndex_from+1)*COL_SIZE]; 
		for(int i=0; i< matrix.length; i++){
			matrix[i] = dataInput.readFloat(); 
		}
		
	}
	@Override
	public void write(DataOutput dataOutput) throws IOException {
		dataOutput.writeLong(objectId); 
		dataOutput.writeInt(rowIndex_from);
		dataOutput.writeInt(rowIndex_to); 
		for(int i=0; i< matrix.length; i++){
			dataOutput.writeFloat(matrix[i]);
		}
	}
	/** Calculates the hashCode of this DoublePoint.
	 * @return returns the hashCode of this DoublePoint
	 */
	public int hashCode() {
		double c = 0;
		for (int i = 0; i < matrix.length; i++)
			c += matrix[i];
		return (int)c%1117;
	}
	/** Returns a string representation of this object.
	 * @return a string representation of the object.
	 */
	public String toString(){
		StringBuffer sb = new StringBuffer("");
		for(int i=0; i<matrix.length; i++){
			sb.append(matrix[i]+"\t");
		}
		return sb.toString();
	}
	@Override
	public int dimensions() {
		// TODO Auto-generated method stub
		throw new RuntimeException(" not support for "+this.getClass().getName());
	}
	@Override
	public Object getPoint() {
		// TODO Auto-generated method stub
		throw new RuntimeException(" not support for "+this.getClass().getName());
	}
	@Override
	public double getValue(int index) {
		
		//return 0;
		throw new RuntimeException(" not support for "+this.getClass().getName()); 
	}
	@Override
	public double distanceTo(Object point) {
		SparseMatrix m=(SparseMatrix)point;
		
		SparseMatrix first;
		SparseMatrix second;
		if (this.rowIndex_from<m.rowIndex_from){
			first=this; 
			second=m; 
		}else {
			first=m; 
			second=this; 
		}
		int join_from=Math.max(this.rowIndex_from, m.rowIndex_from); 		
		int join_to=Math.min(this.rowIndex_to, m.rowIndex_to);
		if (join_to<join_from){ // no overlap; 
			double distance=0; 
			for (int i=0; i<first.matrix.length; i++){
				distance+=first.matrix[i]; 
			}
			for (int i=0; i<second.matrix.length; i++){
				distance+=second.matrix[i]; 
			}
			return distance; 
		}
		join_from-=first.rowIndex_from;
		join_to-=first.rowIndex_from;
		double distance=0;
		double d;    
		int offset_second=(second.rowIndex_from-first.rowIndex_from)*COL_SIZE;
		for (int row=join_from; row<=join_to; row++){
			int offset=row*COL_SIZE; 
			for (int col=0; col<COL_SIZE; col++){
				int pos=offset+col; 
				d=first.matrix[pos]-second.matrix[pos-offset_second];
				distance+=(d>0?d:-d); 
			}
		}
		for (int row=0; row<join_from; row++){
			int offset=row*COL_SIZE; 
			for (int col=0; col<COL_SIZE; col++){
				distance+=first.matrix[offset+col]; 
			}
		}
		int to=first.rowIndex_to-first.rowIndex_from; 
		for (int row=join_to+1; row<=to; row++){
			int offset=row*COL_SIZE; 
			for (int col=0; col<COL_SIZE; col++){
				distance+=first.matrix[offset+col]; 
			}			
		}
		to=second.rowIndex_to-first.rowIndex_from; 
		for (int row=join_to+1; row<=to; row++){
			int offset=row*COL_SIZE; 
			for (int col=0; col<COL_SIZE; col++){
				distance+=second.matrix[offset+col-offset_second]; 
			}			
		}
		return distance; 
	}
}
